using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Media.Imaging;

namespace RevitForge.Services
{
    /// <summary>
    /// 动态命令加载器，用于在运行时编译和加载Revit命令
    /// </summary>
    public class DynamicCommandLoader
    {
        // 存储源代码文件的目录路径
        private readonly string _sourceDirectory;
        // Revit应用程序实例
        private readonly UIApplication _application;
        // Revit功能区面板
        private readonly RibbonPanel _panel;
        private readonly Dictionary<string, PushButton> _commandButtons = new Dictionary<string, PushButton>();

        // 初始化动态命令加载器
        public DynamicCommandLoader(UIApplication application)
        {
            _application = application;
            // 获取RevitForge选项卡下的所有面板
            List<RibbonPanel> panels = application.GetRibbonPanels("RevitForge");
            // 获取名为"自定义"的面板
            _panel = panels.FirstOrDefault(p => p.Name == "自定义");
            // 设置源代码目录路径，位于当前程序集所在目录下的Source文件夹
            _sourceDirectory = Path.Combine(
                Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                "Source");
        }

        // 刷新并加载动态命令，包括加载刷新按钮和所有动态命令
        public void RefreshCommands()
        {
            if (!Directory.Exists(_sourceDirectory))
            {
                Directory.CreateDirectory(_sourceDirectory);
                return;
            }
            string[] txtFiles = Directory.GetFiles(_sourceDirectory, "*.txt");
            foreach (string txtFile in txtFiles)
            {
                try
                {
                    string commandName = Path.GetFileNameWithoutExtension(txtFile);
                    string dllPath = CompileCommand(txtFile);
                    if (!string.IsNullOrEmpty(dllPath))
                    {
                        CreateOrUpdateCommandButton(dllPath, commandName);
                    }
                }
                catch (Exception ex)
                {
                    System.Windows.MessageBox.Show($"加载命令失败: {ex.Message}");
                }
            }
        }

        // 将源代码txt文件编译为.NET程序集。sourceFile源代码txt文件的路径
        private string CompileCommand(string sourceFile)
        {
            string sourceCode = File.ReadAllText(sourceFile);
            CSharpCompilationOptions compilationOptions = new CSharpCompilationOptions(
                OutputKind.DynamicallyLinkedLibrary,
                optimizationLevel: OptimizationLevel.Release,
                allowUnsafe: true);
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
            List<MetadataReference> references = new List<MetadataReference>
            {
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(UIApplication).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Document).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(System.Windows.MessageBox).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(System.Windows.Media.Imaging.BitmapImage).Assembly.Location),
                MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location),
                MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location),
                MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(typeof(UIApplication).Assembly.Location), "RevitAPI.dll")),
                MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(typeof(UIApplication).Assembly.Location), "RevitAPIUI.dll")),
                MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(System.Windows.Media.Visual).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(System.Windows.DependencyObject).Assembly.Location),
                MetadataReference.CreateFromFile(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "RevitForge.dll")),
            };
            string assemblyName = Path.GetFileNameWithoutExtension(sourceFile);
            if (string.IsNullOrEmpty(assemblyName))
            {
                throw new ArgumentException("程序集名称不能为空", nameof(assemblyName));
            }

            // 生成带时间戳的DLL文件名
            string binDirectory = Path.Combine(_sourceDirectory, "bin");
            if (!Directory.Exists(binDirectory))
            {
                Directory.CreateDirectory(binDirectory);
            }
            string dllPath = Path.Combine(binDirectory, $"{assemblyName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.dll");
            CSharpCompilation compilation = CSharpCompilation.Create(
                assemblyName,
                new[] { syntaxTree },
                references,
                compilationOptions);
            Microsoft.CodeAnalysis.Emit.EmitResult result = compilation.Emit(dllPath);

            if (!result.Success)
            {
                IEnumerable<Diagnostic> failures = result.Diagnostics
                    .Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
                string errorMessage = string.Join("\n", failures.Select(x => x.GetMessage()));
                throw new Exception($"编译错误:\n{errorMessage}");
            }
            // 返回DLL路径
            return dllPath;
        }

        // 为编译后的命令创建按钮并添加到Revit功能区
        private void CreateOrUpdateCommandButton(string dllPath, string commandName)
        {
            Assembly assembly = Assembly.LoadFrom(dllPath);
            Type commandType = assembly.GetTypes()
                .FirstOrDefault(t => typeof(IExternalCommand).IsAssignableFrom(t) && !t.IsAbstract);
            if (commandType == null)
            {
                throw new Exception($"在程序集中未找到IExternalCommand的实现: {assembly.FullName}");
            }
            // 隐藏现有按钮
            List<PushButton> existingButtons = _panel.GetItems()
                .OfType<PushButton>()
                .Where(b => b.Name.StartsWith(commandName))
                .ToList();
            foreach (PushButton existingButton in existingButtons)
            {
                existingButton.Visible = false;
                //System.Windows.MessageBox.Show($"已隐藏旧按钮:\n名称: {existingButton.Name}\n程序集: {existingButton.AssemblyName}");
            }

            // 生成带时间戳的唯一按钮名称
            string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
            string uniqueButtonName = $"{commandName}_{timestamp}";

            // 确保使用完整的程序集路径
            if (!Path.IsPathRooted(dllPath))
            {
                dllPath = Path.GetFullPath(dllPath);
            }
            // 创建新按钮
            PushButtonData buttonData = new PushButtonData(
                uniqueButtonName,      // 使用唯一的内部名称
                commandName,           // 先使用文件名作为显示名称
                dllPath,               // 使用完整路径
                commandType.FullName); // 完整的类型名称
            try
            {
                string iconPath = Path.Combine(_sourceDirectory, $"{commandName}.png");
                if (!File.Exists(iconPath))
                {
                    iconPath = Path.Combine(
                        Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                        "Source",
                        "code.png"
                    );
                }
                if (File.Exists(iconPath))
                {
                    BitmapImage icon = new BitmapImage(new Uri(iconPath, UriKind.Absolute));
                    buttonData.LargeImage = icon;
                }
            }

            catch (Exception ex)
            {
                System.Windows.MessageBox.Show($"加载图标时出错: {ex.Message}");
            }
            // 添加按钮并设置属性
            PushButton newButton = _panel.AddItem(buttonData) as PushButton;
            try
            {
                string customButtonName = GetCommandButtonName(commandType);
                if (!string.IsNullOrEmpty(customButtonName))
                {
                    newButton.ItemText = customButtonName;
                }
                string description = GetCommandDescription(commandType);
                if (!string.IsNullOrEmpty(description))
                {
                    newButton.ToolTip = description;
                }
            }
            catch (Exception)
            {
                // 如果设置自定义名称失败，保持原来的文件名
            }
        }
        // 获取命令的描述信息(Description属性)
        private string GetCommandDescription(Type commandType)
        {
            try
            {
                if (Activator.CreateInstance(commandType) is IExternalCommand command)
                {
                    var descriptionProperty = commandType.GetProperty("Description");
                    if (descriptionProperty != null)
                    {
                        return descriptionProperty.GetValue(command) as string ?? $"动态加载的命令: {commandType.Name}";
                    }
                }
            }
            catch (Exception)
            {
                // 如果无法创建实例或获取描述，使用默认描述
            }
            return $"动态加载的命令: {commandType.Name}";
        }
        // 获取命令的按钮名称(ButtonName属性)
        private string GetCommandButtonName(Type commandType)
        {
            try
            {
                if (Activator.CreateInstance(commandType) is IExternalCommand command)
                {
                    var buttonNameProperty = commandType.GetProperty("ButtonName");
                    if (buttonNameProperty != null)
                    {
                        string buttonName = buttonNameProperty.GetValue(command) as string;
                        if (!string.IsNullOrEmpty(buttonName))
                        {
                            // 在前两个字符后添加换行符
                            if (buttonName.Length > 2)
                            {
                                return buttonName.Substring(0, 2) + "\n" + buttonName.Substring(2);
                            }
                            return buttonName;
                        }
                    }
                }
            }
            catch (Exception)
            {
                // 如果无法创建实例或获取按钮名称，使用类型名称作为默认值
            }
            return commandType.Name;
        }
    }
}